/*
	Copyright (C) 2009 Salil Bhagurkar
	
	This file is part of ViSE
	
	ViSE is free software: you can redistribute it and/or modify
	it under the terms of the GNU Lesser General Public License as published by
	the Free Software Foundation, either version 3 of the License, or
	(at your option) any later version.
	
	ViSE is distributed in the hope that it will be useful,
	but WITHOUT ANY WARRANTY; without even the implied warranty of
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
	GNU Lesser General Public License for more details.
	
	You should have received a copy of the GNU Lesser General Public License
	along with ViSE.  If not, see <http://www.gnu.org/licenses/>.
*/

#include <shared.h>

/*This file defines a central server/client that interfaces with the user for text data.*/

int user_init(struct node_fs *this);

struct node_fs user = {
	.type = TYPE_FS,
	.name = "usr",
	.init = user_init,
	.deinit = dummy,
	NODE_FS_INIT_VARS,
	NODE_FS_OPS_INIT,
	NODE_FS_LIST_INIT
};


struct node_fs *waiting_task;
int device_event(struct node_fs *this, void *buf, u32 offset, u32 count);
struct node_fs device_listener = {
	.type = TYPE_FS,
	.name = "device_listener",
	NODE_FS_INIT_INIT,
	.read = dummy,
	.write = device_event,
	.enumerate = dummy,
	.notify = dummy,
	NODE_FS_INIT_VARS,
	NODE_FS_LIST_INIT,
};


static struct node_fs *input_device, *output_device;


#define MAX_BUF 256
static int cur_buf = 0;
static char kbuf[MAX_BUF];
int device_event(struct node_fs *this, void *buf, u32 offset, u32 count)
{
	struct node_fs *kbd;
	kbd = input_device;
	while(count--) {
		kbd->read(kbd, &kbuf[cur_buf], 0, 1);
		if(kbuf[cur_buf] == '\n') {
			fs_write("/usr/msg", &kbuf[cur_buf], 0, 1);
			kbuf[cur_buf] = 0;
			fs_noparse_move(fs_get("/sched/tasks"), waiting_task);
			fs_write("/sched", NULL, 0, 0);
		} else if(kbuf[cur_buf] == '\b') {
			if(cur_buf > 0) {
				fs_write("/usr/msg", &kbuf[cur_buf], 0, 1);
				kbuf[cur_buf--] = 0;
				kbuf[cur_buf] = 0;
			}
		} else if(cur_buf < (MAX_BUF-1)) {
			fs_write("/usr/msg", &kbuf[cur_buf], 0, 1);
			cur_buf++;
		}
	}
	return 0;
}

/*Read a message from usr*/
int user_msg_read(struct node_fs *this, void *buf, u32 offset, u32 count)
{
	fs_noparse_link(input_device, &device_listener);
	waiting_task = current->fs;
	cur_buf = 0;
	memset(kbuf, 0, MAX_BUF);
	fs_noparse_move(fs_get("/sched/waiting"), current->fs);
	fs_write("/sched", NULL, 0, 0);
	memcpy(buf, kbuf, count);
	fs_noparse_unlink(input_device, &device_listener);
	return 0;
}

/*Write a message for user on the console*/
int user_msg_write(struct node_fs *this, void *buf, u32 offset, u32 count)
{
	output_device->write(output_device, buf, offset, count);
	return 0;
}

/*This is used to accept and send messages from and to the console.*/
struct node_fs user_msg = {
	.type = TYPE_FS_BOTH,
	.name = "msg",
	NODE_FS_INIT_INIT,
	.read = user_msg_read,
	.write = user_msg_write,
	.enumerate = dummy,
	.notify = dummy,
	.class = NULL,
	NODE_FS_INIT_VARS,
	NODE_FS_LIST_INIT
};


int user_nl_write(struct node_fs *this, void *buf, u32 offset, u32 count)
{
	printk("\n");
	return 0;
}

struct node_fs user_nl = {
	.type = TYPE_FS_CLIENT,
	.name = "nl",
	NODE_FS_INIT_INIT,
	.read = dummy,
	.write = user_nl_write,
	.enumerate = dummy,
	.notify = dummy,
	.class = NULL,
	NODE_FS_INIT_VARS,
	NODE_FS_LIST_INIT,
};

/*read and write numbers from user*/
int user_num_read(struct node_fs *this, void *buf, u32 offset, u32 count)
{
	return 0;
}

int user_num_write(struct node_fs *this, void *buf, u32 offset, u32 count)
{
	u32 *num;
	num = (u32 *)buf;
	printk("%d", *num);
	return 0;
}

/*This is used to accept and send numbers from and to the console*/
struct node_fs user_num = {
	.type = TYPE_FS_BOTH,
	.name = "num",
	NODE_FS_INIT_INIT,
	.read = user_num_read,
	.write = user_num_write,
	.enumerate = dummy,
	.notify = dummy,
	.class = NULL,
	NODE_FS_INIT_VARS,
	NODE_FS_LIST_INIT
};


//Find input and output devices
int user_init(struct node_fs *this)
{
	struct node_fs *console_dev;
	printk("Finding console devices...\n");
	input_device = output_device = NULL;
	for(console_dev = (fs_get("/class/console")->links); console_dev; console_dev = console_dev->next) {
		if(console_dev->type & TYPE_FS_SERVER) {
			input_device = console_dev;
			printk("input_device = %s\n", console_dev->name);
		} else if(console_dev->type & TYPE_FS_CLIENT) {
			output_device = console_dev;
			printk("output_device = %s\n", console_dev->name);
		}
		if(input_device && output_device)
			break;
	}
}


void init_user()
{
	fs_link("/", &user);
	fs_link("/usr", &user_msg);
	fs_link("/usr", &user_num);
	fs_link("/usr", &user_nl);
	user.init(&user);
}
